<?php
/**
 * 微信用户中间件
 * Author: lyun
 * Date: 2018/4/11
 * Created by Panxsoft.
 */

namespace App\Http\Middleware;

use App\Exceptions\ApiErrCode;
use App\Exceptions\ApiException;
use App\Models\OpenWechat\Authorizer;
use App\Models\OpenWechat\WechatUser;
use App\Services\OpenWechat\AppFactory;
use App\Services\OpenWechat\Mock;
use App\Services\OpenWechat\WechatUserRepository;
use App\Services\Utils\ApiChecker;
use App\Services\Locale\Language;
use Closure;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;

class WechatAuth {

    use ApiChecker;
    
    /** @var WechatUser $wechat_user */
    public static $wechat_user;

    /** @var  Authorizer $authorizer */
    public static $authorizer;

    /** @var string $customer_id */
    public static $customer_id;

    /**
     *
     * @example composer require tymon/jwt-auth@1.0.0-beta.3
     * @link http://jwt-auth.readthedocs.io/en/docs/quick-start/
     *
     * @param Request $request
     * @param Closure $next
     * @return mixed
     * @throws \Exception
     */
    public function handle(Request $request, Closure $next)
    {
        $wechat_user = $this->checkGetWechatOrWebUser($request);

        Language::setAppLocale($wechat_user->language);

        $this->setWechatAuthGuards($wechat_user);

        return $next($request);
    }
    
    /**
     * @param Request $request
     *
     * @return WechatUser
     * @throws \Exception
     */
    private function checkGetWechatUser(Request $request): WechatUser
    {
        $appid = $request->appid;
            $this->checkParamExist($appid, trans('middleware.appid'));
            
        $openid = $request->openid;
            $this->checkParamExist($openid, trans('middleware.openid'));

        /** @var Authorizer $author */
        $author = Authorizer::where('authorizer_appid', $appid)->first();
            $this->checkIsTrue($author, trans('middleware.authorizer_not_found'));
            $this->checkIsTrue($author->isAuthorized(), trans('middleware.authorizer_canceled'));

        /** @var WechatUser $wechat_user */
        $wechat_user = $author->wechatUsers()
            ->where('openid', $openid)
            ->first();

        if (! $wechat_user) {
            // 没有 $wechat_user 时必须成功查询并保存到 wechat_user 表
            $wechat_user = $this->querySaveWechatUser($author, $openid);
        } else {
            // 本地测试不强制更新用户信息，线上也有从网址授权公众号的未订阅的
            $wechat_user = $this->tryUpdateWechatUser($author, $wechat_user);
        }
            $this->checkIsTrue($wechat_user, trans('middleware.error', ['openid' =>$openid]));

        self::$customer_id = ($wechat_user->stores->first()->customer_id) ?? 0;

        return $wechat_user;
    }

    /**
     * @param $web_openid
     *
     * @return WechatUser
     * @throws \Exception
     */
    private function checkGetWebUser($web_openid): WechatUser
    {
        $wechat_user = (new WechatUser)
            ->where('openid', $web_openid)
            ->first();
            $this->checkItemExist($wechat_user, trans('middleware.wechat_user_not_found_from_web_openid'));

        self::$customer_id = $wechat_user->customer_id ?? 0;

        return $wechat_user;
    }

    /**
     * 可能存在之前订阅的客户， 尝试用 easy wechat 重新获取信息
     *
     * @param Authorizer $author
     * @param string $openid
     * @return WechatUser|null
     * @throws \Exception
     */
    private function tryAppQuerySaveWechatUser(Authorizer $author, string $openid): ?WechatUser
    {
        try {
            return $this->querySaveWechatUser($author, $openid);
        } catch (\Exception $e) {
            if (! config('app.debug')) logger($e->getMessage());

            return null;
        }
    }

    /**
     * @param Authorizer $author
     * @param string $openid
     *
     * @return WechatUser
     * @throws InvalidConfigException
     */
    private function querySaveWechatUser(Authorizer $author, string $openid): WechatUser
    {
        $user_info = $this->appQueryWechatUser($author, $openid);

        $user_info['authorizer_id'] = $author->id;
        $wechat_user = (new WechatUserRepository())
            ->createOrUpdateWechatUserInfo($user_info);

        return $wechat_user;
    }

    /**
     * @param Request $request
     *
     * @return WechatUser
     * @throws \Exception
     */
    private function checkGetWechatOrWebUser(Request $request): WechatUser
    {
        $openid = $request->openid;

        if ($this->isWebOpenid($openid)) { // h5网页登录
            $wechat_user = $this->checkGetWebUser($openid);
        } else {  // 微信用户的登录
            $wechat_user = $this->checkGetWechatUser($request);
        }

        return $wechat_user;
    }

    /**
     * @param $openid
     *
     * @return bool
     */
    private function isWebOpenid(string $openid = null): bool
    {
        return $openid && starts_with($openid, WechatUser::WEB_OPENID_PREFIX);
    }

    /**
     * @param $wechat_user
     */
    private function setWechatAuthGuards(WechatUser $wechat_user): void
    {
        self::$authorizer = $wechat_user->authorizer ?? null;

        self::$wechat_user = $wechat_user;

        $wechat_user->addHidden('authorizer');
    }

    /**
     * @param Authorizer $author
     *
     * @param WechatUser $wechat_user
     *
     * @return WechatUser|null
     * @throws \Exception
     */
    private function tryUpdateWechatUser(Authorizer $author,
                                         WechatUser $wechat_user): WechatUser
    {
        if (! $wechat_user->needQueryUpdateInfo()) {
            return $wechat_user;
        }

        $query_user = $this->tryAppQuerySaveWechatUser($author, $wechat_user->openid);

        $wechat_user = $query_user ?? $wechat_user;

        return $wechat_user;
    }

    /**
     * @param Authorizer $author
     * @param int $subscribe
     */
    private function checkWechatUserSubscribe(Authorizer $author, int $subscribe): void
    {
        if ($subscribe !== WechatUser::SUBSCRIBED) {
            throw new ApiException(
                ApiErrCode::UNSUBSCRIBED_OFFICIAL_ACCOUNT,
                trans('checker.unsubscribed_official_account', ['tip'=> $author->nick_name])
            );
        }
    }

    /**
     * @param Authorizer $author
     * @param string $openid
     *
     * @return array
     * @throws InvalidConfigException
     */
    private function appQueryWechatUser(Authorizer $author, string $openid): array
    {
        $app = (new AppFactory())->authorizerApp($author->authorizer_appid);

        // 看下如果线上报错要不要处理
        $user_info = $app->user->get($openid);
        /**
         * @see Mock::appUserGetInvalidOpenId()
         */
        if (isset($user_info['errcode'])) { // 微信的接口报错
            throw new ApiException($user_info['errcode'], $user_info['errmsg']);
        }
        /**
         * @see Mock::appUserUnSubscribedGet()
         */
        $this->checkWechatUserSubscribe($author, $user_info['subscribe']);

        return $user_info;
    }
}